Springboot入门
SpringBoot简介
简化Spring应用开发的一个框架
整个Spring技术栈的一个大整合
J2EE开发的一站式解决方案
微服务
2014,martin fowler
微服务:架构风格(服务微化)
一个应用应该是一组小型服务,可以通过HTTP的方式进行互通。
单体应用:ALL IN ONE
微服务:每一个功能元素最终都是一个可独立替换和独立升级的软件单元;
HelloWorld
功能:浏览器发送hello请求,服务器接受请求并处理,响应“Hello World!”字符串。
创建maven项目
导入依赖
1 | <?xml version="1.0" encoding="UTF-8"?> |
编写主程序
1 | /** |
编写Controller、Service
1 |
|
运行
直接右键运行主程序类即可
打包
maven中导入插件
1
2
3
4
5
6
7
8
9<!-- 可将应用打包成可执行jar包的插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>点击idea右侧工具栏中的Maven,运行”项目名-Lifecycle-package“
将target目录下jar包拷出,使用cmd的”java -jar xxx.jar“运行即可
HelloWorld探究
POM文件
父项目
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
</parent>
他的父项目是:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐dependencies</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath>../../spring‐boot‐dependencies</relativePath>
</parent>
他来真正管理Spring Boot应用里面的所有依赖版本
Spring Boot的版本仲裁中心。
以后我们导入依赖默认是不需要写版本(没有在dependencies里面管理的依赖自然需要声明版本号)
启动器
1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter:springboot的场景启动器,帮我们导入了指定模块(如:web)正常运行所依赖的组件。
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter,相关场景的所有依赖都会导入进来。需要什么功能就导入什么场景启动器
主程序类
1 | /** |
@SpringBootApplication:用来标注SpringBoot的主程序类,SpringBoot
应该运行这个类的main方法来启动SpringBoot应用。
1 | ({ElementType.TYPE}) |
@SpringBootConfiguration:标注SpringBoot的配置类
- @Configuration:标注配置类(Spring)
@EnableAutoConfiguration:开启自动配置
1
2
3
.class}) ({AutoConfigurationImportSelector
public @interface EnableAutoConfiguration {...}
@AutoConfigurationPackage:自动配置包
1
2
3.class}) ({Registrar
public @interface AutoConfigurationPackage {
}
1
2
3
4
5
6
7
8
9
10
11
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
**将主配置类所在包及下面所有子包里面的所有组件扫描到Spring容器。**
@Import({AutoConfigurationImportSelector.class})
AutoConfigurationImportSelector:导入哪些组件的选择器。
将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。
会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件。
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们。
使用Spring Initializer快速创建SpringBoot项目
IDE都支持使用Spring的项目创建向导快速创建一个Spring Boot项目
选择我们需要的模块;向导会联网创建Spring Boot项目
默认生成的Spring Boot项目
主程序已经生成好了,我们只需要我们自己的逻辑
resources文件夹中目录结构
static:保存所有的静态资源(js/css/images)
templates:保存所有的模板页面(SpringBoot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面,可以使用模板引擎(如:freemarker、thymeleaf))
application.properties:SpringBoot应用的配置文件,可以修改一些默认设置
配置文件
配置文件
Spring Boot使用全局配置文件,配置文件名是固定的:
- application.properties
- application.yml
配置文件作用:修改Spring Boot在底层封装好的默认值。
YAML(YAML AIN’T Markup Language):
是一个标记语言,
又不是一个标记语言。
以前的配置文件,大多数使用的是 xxx.xml文件。
yaml以数据为中心,比json、xml等更适合做配置文件。
YMAL:
1
2server:
port: 9000xml:
1
2
3<server>
<port>9000</port>
</server>
YAML语法
基本语法
k:(空格)v:表示一堆键值对(空格必须有);
以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一层级的:
1 | server: |
属性和值也是大小写敏感。
值的写法
字面量:普通的值(数字,字符串,布尔)
k: v:字面量直接来写
字符串默认不用加上单引号或者双引号
“”:双引号 不会转义字符串里的特殊字符;特殊字符会作为本身想要表示的意思如:
name:"zhangsan\n lisi"
输出:zhangsan换行 lisi
‘’:单引号 会转义特殊字符,特殊字符最终只是一个普通的字符串数据
如:
name:'zhangsan\n lisi'
输出:zhangsan\n lisi
对象、Map(属性和值)键值对:
1
2
3frends:
lastName: zhangsan
age: 20
行内写法:
1 | friends: {lastName: zhangsan,age: 18} |
数组(List、Set): 用-表示数组中的一个元素
1
2
3
4pets:
‐ cat
‐ dog
‐ pig
行内写法:
1 | pets: [cat,dog,pig] |
组合变量:
多个组合到一起
配置文件值注入
@ConfigurationProperties
application.yml 配置文件
1
2
3
4
5
6
7
8
9
10
11
12person:
age: 18
boss: false
birth: 2017/12/12
maps: {k1: v1,k2: 12}
lists:
- lisi
- zhaoliu
dog:
name: wangwang
age: 2
last-name: wanghuahuaapplication.properties 配置文件
1
2
3
4
5
6
7
812 =
false =
张三 =
v1 =
v2 =
a,b,c =
wanghuahu =
15 =
idea配置文件编码为utf-8,properties文件编码默认GBK,所以中文输出乱码。
解决方法:settings → file encoding → [property → utf-8,勾选转成ascii]
javaBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
* 将配置文件的配置每个属性的值,映射到组件中
* @ConfigurationProperties:告诉SpringBoot将文本的所有属性和配置文件中的相关配置进行绑定
* prefix = "person" 与配置文件进行一一映射
*
* 只有这个组件是容器中的组件,才能提供到容器中
*/
"person") (prefix =
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
导入配置文件处理器,以后编写配置就有提示了
1 | <dependency> |
@Value注解
1 |
|
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件属性 | 单个指定 |
松散绑定(语法) | 支持 | 不支持 |
spEL | 不支持 | 支持 |
JSR303校验 | 支持 | 不支持 |
复杂类型 | 支持 | 不支持 |
松散语法:javaBean中last-name(或者lastName) → application.properties中的last-name
spEL语法:#{11*2}
JSR303:@Value会直接忽略,校验规则
JSR303校验:
1 |
|
复杂类型:
1 |
|
使用场景分析:
如果我们只是在某个业务逻辑中获取一下配置文件的某一项值,使用@Value
如果专门编写了一个javaBean和配置文件进行映射,我们直接使用@ConfigurationProperties
例如:
编写新的Controller文件
1
2
3
4
5
6
7
8
9
10
11
public class HelloController {
"${person.last-name}") (
private String name;
"/hello") (
public String sayHello() {
return "Hello"+ name;
}
}配置文件
1
2
3
4
5
6
7
812 =
false =
李四 =
v1 =
v2 =
a,b,c =
wanghuahu =
15 =运行结果
Hello 李四
其他注解
@PropertySource:
作用:加载指定的properties配置文件
举例:
新建一个person.properties文件
1
2
3
4
5
6
7
812 =
false =
李四 =
v1 =
v2 =
a,b,c =
wanghuahu =
15 =
在javaBean中加入@PropertySource注解
1
2
3
4
5
6"classpath:person.properties"}) (value = {
"person") (prefix =
public class Person {
private String lastName;
}
@ImportResource
作用:导入Spring配置文件,并且让这个配置文件生效
举例:
新建一个Spring的配置文件,bean.xml
1
2
3
4
5
6
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="HelloService" class="com.wdjr.springboot.service.HelloService"></bean>
</beans>
编写测试类,检查容器是否加载Spring配置文件写的bean
1
2
3
4
5
6
7
8
ApplicationContext ioc;
public void testHelloService(){
boolean b = ioc.containsBean("HelloService");
System.out.println(b);
}
运行结果
false
使用@ImportResource注解
将@ImportResource标注在主配置类上
1
2
3
4
5
6
7
8
9"classpath:beans.xml"}) (locations={
public class SpringBoot02ConfigApplication {
public static void main(String[] args {
SpringApplication.run(SpringBoot02ConfigApplication.class, args);
}
}
运行结果
true
缺点:每次指定xml文件太麻烦
SpringBoot推荐给容器添加组件的方式(@Configuration + @Bean):
1 | /** |
1 |
|
配置文件占位符
随机数
1 | 、${random.int}、${random.long}、 |
获取配置值
1 | ${random.int} = |
存在以下两种情况:
没有声明person.last-name
会报错,新声明的需要加默认值。
1 | ${random.int} = |
输出结果:hello's wanghuahua
Profile
多Profile文件
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml
- application.properties
- application-dev.properties
- application-prod.properties
默认使用application.properties
application.properties配置文件指定
1 | dev = |
YAML文档块
1 | server: |
激活指定profile
在配置文件中激活
1
dev =
命令行
java -jar xxx.jar --spring.profiles.active=dev
虚拟机参数
-Dspring.profiles.active=dev
加载配置文件位置
SpringBoot启动扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
- file:./config/
- file./
- classpath:/config/
- classpath:/
优先级从高到低顺序,高优先级会覆盖低优先级的相同配置,互补配置。
也可以通过spring.config.location来改变默认配置文件位置 。
项目打包好了以后,可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认的配置文件会共同起作用,互补配置。
java -jar xxx.jar --spring.config.location=E:/work/application.properties
运维比较有用,从外部加载,不用修改别的文件
外部配置的加载顺序
SpringBoot也可以从以下位置加载配置,优先级从高到低,高优先级覆盖低优先级,可以互补。
命令行参数
java -jar xxx.jar –server.port=9005 –server.context-path=/abc
来自java:comp/env的JNDI属性
java系统属性(System.getProperties())
操作系统环境变量
RandomValuePropertySource配置的random.*属性值
jar包外部的application-{profile}.properties 或 application.yml(带Spring.profile)配置文件
jar包内部的application-{profile}.properties 或 application.yml(带Spring.profile)配置文件
jar包外部的application.properties 或 application.yml(带Spring.profile)配置文件
jar包内部的application.properties 或 application.yml(不带spring.profile)配置文件
@Configuration注解类的@PropertySource
通过SpringApplication.setDefaultProperties指定的默认属性
自动配置
自动配置原理
SpringBoot启动的时候加载主配置类,开启自动配置功能,@EnableAutoConfiguration
@EnableAutoConfiguration 作用:
利用AutoConfigurationImportSelector给容器中导入一些组件?
可以查看selectImports()方法的内容
List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置
1
2
3
4SpringFactoriesLoader.loadFactoryNames()
扫描所有jar包类路径下的 MATA-INF/spring.factories
把扫描到的这些文件的内容包装成properties对象
从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加到容器中
将类路径下 MATE-INF/spring.factories里面配置的所有的EnableAutoConfiguration的值加入到了容器中。
每一个自动配置类进行自动配置功能
以HttpEncodingAutoConfiguration 为例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30//表示是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
.class})//启动指定类的Configurationproperties功能;将配置文件中的值和HttpEncodingProperties绑定起来了;并把HttpEncodingProperties加入ioc容器中 ({HttpEncodingProperties
@ConditionalOnWebApplication//根据不同的条件,进行判断,如果满足条件,整个配置类里面的配置就会失效,判断是否为web应用;
(
type = Type.SERVLET
)
.class})//判断当前项目有没有这个类,解决乱码的过滤器 ({CharacterEncodingFilter
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)//判断配置文件是否存在某个配置 spring.http.encoding,matchIfMissing = true如果不存在也是成立,即使不配置也生效
public class HttpEncodingAutoConfiguration {
//给容器添加组件,这个组件的值需要从properties属性中获取
private final HttpEncodingProperties properties;
//只有一个有参数构造器情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpEncodingProperties.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpEncodingProperties.Type.RESPONSE));
return filter;
}所有在配置文件中能配置的属性都是在xxxProperties类中封装着,配置文件能配置什么就可以参照某个功能对应的这个属性类。
1
2
3"spring.http.encoding") // 从配置文件中的值进行绑定和bean属性进行绑定 (prefix =
public class HttpEncodingProperties {...}
根据当前不同条件判断,决定这个配置类是否生效?
一旦这个配置类生效;这个配置类会给容器添加各种组件;这些组件的属性是从对应的properties中获取的,这些类里面的每个属性又是和配置文件绑定的。
所有自动配置组件
每一个xxxAutoConfiguration这样的类都是容器中的一个组件,都加入到容器中。
作用:用它们做自动配置。
1 | # Auto Configure |
精髓
SpringBoot启动会加载大量的自动配置类
我们看我们需要的功能有没有SpringBoot默认写好的自动配置类
如果有,再看这个自动配置类中配置了哪些组件(只要有需要用的组件,就不需要再手动配置);如果有,则需要自己手动配置。
给容器中自动配置添加组件的时候,会从properties类中获取属性。我们就可以在配置文件中指定这些属性的值
xxxAutoConfiguration:自动配置类,给容器中添加组件。
xxxProperties:封装配置文件中的属性。
细节
@Conditional派生注解
利用Spring注解版原生的@Conditional注解
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效。
@Conditional派生注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 满足spEL表达式 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定的资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
自动配置报告
自动配置类必须在一定条件下生效。
我们可以通过启用
debug=true
属性,配置文件,打印自动配合报告,这样就可以知道自动配置类生效。自动配置报告:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:(启动的,匹配成功的)
-----------------
CodecsAutoConfiguration matched:
- class 'org.springframework.http.codec.CodecConfigurer'; did not find unwanted class (OnClassCondition) found required
......
Negative matches:(没有启动的,没有匹配成功的)
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
......
日志
日志框架
日志抽象层 | 日志实现 |
---|---|
Log4j |
选用SLF4J + Logback
SpringBoot的底层是Spring框架,使用的JCL,SpringBoot改用了SLF4J。
SLF4J的使用
如何在系统中使用SLF4J
以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法。
应该给系统里面导入slf4j的jar包和logback的实现jar包。
1 | import org.slf4j.Logger; |
每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架的配置文件。
遗留问题
如开发a系统(slf4j+logback):Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis……
c
如何让系统中所有日志统一到slf4j?
将系统中其他日志框架排除出去
用中间包来替换原有的日志框架
导入slf4j的其他实现
SpringBoot日志关系
SpringBoot的日志功能:
1 | <dependency> |
总结:
SpringBoot底层也是使用slf4jJ+logback
SpringBoot也把其他日志替换成了slf4j
起着commons.loggings的名字其实new的SLF4J替换中间包
如果要引入其他框架?一定要把这个框架的默认日志依赖移除掉
日志使用
默认配置
trace-debug-info-warn-error
可以调整需要的日志级别进行输出,不用注释语句。
1 | // 记录器 |
调整指定包的日志级别在配置文件中进行配置:
1 | logging.level.com.gxk=trace |
日志输出格式:
1 | #控制台输出的日志格式 |
SpringBoot修改日志的默认配置:
1 | logging.level.com.gxk=trace |
指定配置
给类路径下放上每个日志框架自己的配置文件,SpringBoot就不会使用默认的配置
logging System | Customization |
---|---|
Logback | logback-spring.xml ,logback-spring.groovy ,logback.xml or logback.groovy |
Log4J2 | log4j2-spring.xml or log4j2.xml |
JDK(Java Util Logging) | logging.properties |
logback.xml
直接被logback日志框架识别 ,logback-spring.xml
先由SpringBoot识别。
1 | <springProfile name="dev"> |
使用参数 --spring.profiles.active=dev
运行,即使用这段配置。
切换日志框架
logback → log4j
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>切换为log4j2的starter
1
2
3
4
5
6
7
8
9
10
11
12
13
14<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>